package math.matrix;

import math.Maths;

public class Matmath 
{
    
    private Matmath() 
    {
    }
    
    public static final mat4f IDENTITY = getIdentityMatrix();
    
    private static mat4f matXtemp = new mat4f();
    private static mat4f matYtemp = new mat4f();
    private static mat4f matZtemp = new mat4f();
    private static mat4f matXYtemp = new mat4f();
    
    public static mat4f getIdentityMatrix() 
    {
        mat4f matrix = new mat4f();
        matrix.setToIdentity();
        return matrix;
    }
    
    public static mat4f getScaleMatrix(float xScale, float yScale, float zScale)
    {
        mat4f matrix = new mat4f();
        Matmath.calcScale(
            xScale, yScale, zScale, 
            matrix
        );
        return matrix;
    }
    
    public static mat4f getRotationMatrix(float xRot, float yRot)
    {
        mat4f matrix = new mat4f();
        Matmath.calcRotationMatXYZ(
            xRot, yRot, 0.0f, 
            matrix
        );
        return matrix;
    }
    
    public static mat4f getTranslationMatrix(float xTrans, float yTrans, float zTrans)
    {
        mat4f matrix = new mat4f();
        Matmath.calcTranslation(
            xTrans, yTrans, zTrans, 
            matrix
        );
        return matrix;
    }
    
    public static void multiply(mat4f m0, mat4f m1, mat4f dest) 
    {
        Matmath.multMat4(
            m0, m1, 
            dest
        );
        dest.calcBuffer();
    }
    
    public static void calcScale(
            float xScale, float yScale, float zScale, 
            mat4f dest)
    {
        Matmath.setToIdentity(dest);
        dest.mat[0] = xScale;
        dest.mat[5] = yScale;
        dest.mat[10] = zScale;
        dest.calcBuffer();
    }
    
    public static void calcOrtho(
            float l, 
            float r, 
            float b, 
            float t, 
            float n, 
            float f, 
            mat4f dest) 
    {
        float dx = r - l;
        float dy = t - b;
        float dz = f - n;
        float ndpx = -r - l;
        float ndpy = -t - b;
        float ndpz = -f - n;
        dest.mat[0] = 2.0f / dx;
        dest.mat[5] = 2.0f / dy;
        dest.mat[10] = -2.0f / dz;
        dest.mat[12] = ndpx / dx;
        dest.mat[13] = ndpy / dy;
        dest.mat[14] = ndpz / dz;
        dest.mat[15] = 1.0f;
        dest.calcBuffer();
    }
    
    public static void calcProjection(
            float l, 
            float r, 
            float b, 
            float t, 
            float n, 
            float f, 
            mat4f dest) 
    {
        Matmath.setToIdentity(dest);
        float rec_dz = 1.0f / (f - n);
        dest.mat[0] = n / r;
        dest.mat[5] = n / t;
        dest.mat[10] = -(f + n) * rec_dz;
        dest.mat[11] = -1.0f;
        dest.mat[14] = -2.0f * f * n * rec_dz;
        dest.calcBuffer();
    }
    
    public static void calcProjection(
            float fov, 
            float aspectRatio, 
            float zNear, 
            float zFar,
            mat4f dest) 
    {
        float fH = Maths.tanDeg(fov * 0.5f) * zNear;
        float fW = fH * aspectRatio;
        Matmath.calcProjection(-fW, fW, -fH, fH, zNear, zFar, dest);
    }
    
    public static void calcTranslation(
            float xTrans, float yTrans, float zTrans, 
            mat4f dest) 
    {
        Matmath.setToIdentity(dest);
        dest.mat[12] = xTrans;
        dest.mat[13] = yTrans;
        dest.mat[14] = zTrans;
        dest.calcBuffer();
    }
    
    public static void calcRotationMatXYZ(
            float xRot, float yRot, float zRot, 
            mat4f dest) 
    {
        Matmath.getRotationMatX(xRot, Matmath.matXtemp);
        Matmath.getRotationMatY(yRot, Matmath.matYtemp);
        Matmath.getRotationMatZ(zRot, Matmath.matZtemp);
        Matmath.multMat4(
            Matmath.matXtemp, Matmath.matYtemp, 
            Matmath.matXYtemp
        );
        Matmath.multMat4(
            Matmath.matXYtemp, Matmath.matZtemp, 
            dest
        );
        dest.calcBuffer();
    }
    
    // No calcBuffer()
    private static void calcRotationMatArb(
            float angleRad, 
            float xAxis, float yAxis, float zAxis, 
            mat4f dest) 
    {
        float halfAngle = angleRad * 0.5f;
        float p = xAxis * Maths.sinRad(halfAngle);
        float q = yAxis * Maths.sinRad(halfAngle);
        float r = zAxis * Maths.sinRad(halfAngle);
        float s = Maths.cosRad(halfAngle);
        float k2pp = 2.0f * p * p;
        float k2qq = 2.0f * q * q;
        float k2rr = 2.0f * r * r;
        float k2pq = 2.0f * p * q;
        float k2qr = 2.0f * q * r;
        float k2rp = 2.0f * r * p;
        float k2ps = 2.0f * p * s;
        float k2qs = 2.0f * q * s;
        float k2rs = 2.0f * r * s;
        dest.mat[0] = 1.0f - k2qq - k2rr;
        dest.mat[1] = k2pq + k2rs;
        dest.mat[2] = k2rp - k2qs;
        dest.mat[4] = k2pq - k2rs;
        dest.mat[5] = 1.0f - k2pp - k2rr;
        dest.mat[6] = k2qr - k2ps;
        dest.mat[8] = k2rp + k2qs;
        dest.mat[9] = k2qr + k2ps;
        dest.mat[10] = 1.0f - k2pp - k2qq;
        dest.mat[15] = 1.0f;
    }
    
    // No calcBuffer()
    private static void getRotationMatX(
            float angleRad, 
            mat4f dest) 
    {
        float halfAngle = angleRad * 0.5f;
        float p = Maths.sinRad(halfAngle);
        float s = Maths.cosRad(halfAngle);
        float k2pp = 2.0f * p * p;
        float k2ps = 2.0f * p * s;
        dest.mat[0] = 1.0f;
        dest.mat[5] = 1.0f - k2pp;
        dest.mat[6] = -k2ps;
        dest.mat[9] = k2ps;
        dest.mat[10] = 1.0f - k2pp;
        dest.mat[15] = 1.0f;
    }
    
    // No calcBuffer()
    private static void getRotationMatY(
            float angleRad, 
            mat4f dest) 
    {
        float halfAngle = angleRad * 0.5f;
        float q = Maths.sinRad(halfAngle);
        float s = Maths.cosRad(halfAngle);
        float k2qq = 2.0f * q * q;
        float k2qs = 2.0f * q * s;
        dest.mat[0] = 1.0f - k2qq;
        dest.mat[2] = -k2qs;
        dest.mat[5] = 1.0f;
        dest.mat[8] = k2qs;
        dest.mat[10] = 1.0f - k2qq;
        dest.mat[15] = 1.0f;
    }
    
    // No calcBuffer()
    private static void getRotationMatZ(
            float angleRad, 
            mat4f dest) 
    {
        float halfAngle = angleRad * 0.5f;
        float r = Maths.sinRad(halfAngle);
        float s = Maths.cosRad(halfAngle);
        float k2rr = 2.0f * r * r;
        float k2rs = 2.0f * r * s;
        dest.mat[0] = 1.0f - k2rr;
        dest.mat[1] = k2rs;
        dest.mat[4] = -k2rs;
        dest.mat[5] = 1.0f - k2rr;
        dest.mat[10] = 1.0f;
        dest.mat[15] = 1.0f;
    }
    
    // No calcBuffer()
    private static void multMat4(
            mat4f m0, mat4f m1, 
            mat4f dest) 
    {
        dest.mat[0]  = m0.mat[0] * m1.mat[0]  + m0.mat[4] * m1.mat[1]  + m0.mat[8] * m1.mat[2]   + m0.mat[12] * m1.mat[3];
        dest.mat[1]  = m0.mat[1] * m1.mat[0]  + m0.mat[5] * m1.mat[1]  + m0.mat[9] * m1.mat[2]   + m0.mat[13] * m1.mat[3];
        dest.mat[2]  = m0.mat[2] * m1.mat[0]  + m0.mat[6] * m1.mat[1]  + m0.mat[10] * m1.mat[2]  + m0.mat[14] * m1.mat[3];
        dest.mat[3]  = m0.mat[3] * m1.mat[0]  + m0.mat[7] * m1.mat[1]  + m0.mat[11] * m1.mat[2]  + m0.mat[15] * m1.mat[3];
        
        dest.mat[4]  = m0.mat[0] * m1.mat[4]  + m0.mat[4] * m1.mat[5]  + m0.mat[8] * m1.mat[6]   + m0.mat[12] * m1.mat[7];
        dest.mat[5]  = m0.mat[1] * m1.mat[4]  + m0.mat[5] * m1.mat[5]  + m0.mat[9] * m1.mat[6]   + m0.mat[13] * m1.mat[7];
        dest.mat[6]  = m0.mat[2] * m1.mat[4]  + m0.mat[6] * m1.mat[5]  + m0.mat[10] * m1.mat[6]  + m0.mat[14] * m1.mat[7];
        dest.mat[7]  = m0.mat[3] * m1.mat[4]  + m0.mat[7] * m1.mat[5]  + m0.mat[11] * m1.mat[6]  + m0.mat[15] * m1.mat[7];
        
        dest.mat[8]  = m0.mat[0] * m1.mat[8]  + m0.mat[4] * m1.mat[9]  + m0.mat[8] * m1.mat[10]  + m0.mat[12] * m1.mat[11];
        dest.mat[9]  = m0.mat[1] * m1.mat[8]  + m0.mat[5] * m1.mat[9]  + m0.mat[9] * m1.mat[10]  + m0.mat[13] * m1.mat[11];
        dest.mat[10] = m0.mat[2] * m1.mat[8]  + m0.mat[6] * m1.mat[9]  + m0.mat[10] * m1.mat[10] + m0.mat[14] * m1.mat[11];
        dest.mat[11] = m0.mat[3] * m1.mat[8]  + m0.mat[7] * m1.mat[9]  + m0.mat[11] * m1.mat[10] + m0.mat[15] * m1.mat[11];
        
        dest.mat[12] = m0.mat[0] * m1.mat[12] + m0.mat[4] * m1.mat[13] + m0.mat[8] * m1.mat[14]  + m0.mat[12] * m1.mat[15];
        dest.mat[13] = m0.mat[1] * m1.mat[12] + m0.mat[5] * m1.mat[13] + m0.mat[9] * m1.mat[14]  + m0.mat[13] * m1.mat[15];
        dest.mat[14] = m0.mat[2] * m1.mat[12] + m0.mat[6] * m1.mat[13] + m0.mat[10] * m1.mat[14] + m0.mat[14] * m1.mat[15];
        dest.mat[15] = m0.mat[3] * m1.mat[12] + m0.mat[7] * m1.mat[13] + m0.mat[11] * m1.mat[14] + m0.mat[15] * m1.mat[15];
    }
    
    // No calcBuffer()
    public static void setTo(mat4f source, mat4f dest) 
    {
        dest.mat[0] = source.mat[0];
        dest.mat[1] = source.mat[1];
        dest.mat[2] = source.mat[2];
        dest.mat[3] = source.mat[3];
        dest.mat[4] = source.mat[4];
        dest.mat[5] = source.mat[5];
        dest.mat[6] = source.mat[6];
        dest.mat[7] = source.mat[7];
        dest.mat[8] = source.mat[8];
        dest.mat[9] = source.mat[9];
        dest.mat[10] = source.mat[10];
        dest.mat[11] = source.mat[11];
        dest.mat[12] = source.mat[12];
        dest.mat[13] = source.mat[13];
        dest.mat[14] = source.mat[14];
        dest.mat[15] = source.mat[15];
    }
    
    // No calcBuffer()
    public static void setToIdentity(mat4f dest) 
    {
        dest.mat[0] = 1.0f;
        dest.mat[1] = 0.0f;
        dest.mat[2] = 0.0f;
        dest.mat[3] = 0.0f;
        dest.mat[4] = 0.0f;
        dest.mat[5] = 1.0f;
        dest.mat[6] = 0.0f;
        dest.mat[7] = 0.0f;
        dest.mat[8] = 0.0f;
        dest.mat[9] = 0.0f;
        dest.mat[10] = 1.0f;
        dest.mat[11] = 0.0f;
        dest.mat[12] = 0.0f;
        dest.mat[13] = 0.0f;
        dest.mat[14] = 0.0f;
        dest.mat[15] = 1.0f;
    }
    
}
